package com.nuonuo.router;

import com.nuonuo.middleware.common.tools.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author duan
 * @version 1.0
 * @date 2023/4/7 9:35
 */
public class TrieRouter implements IRouter {

    private static final Logger logger = LoggerFactory.getLogger(TrieRouter.class.getName());

    private Node root;

    public TrieRouter() {
        root = new Node();
    }

    static class Node {

        String location = null;
        final Map<String, Node> subnodes = new HashMap<String, Node>();

        void setLocation(String location) {
            this.location = location;
        }

        @Override
        public String toString() {
            StringBuilder temp = new StringBuilder(
                    (location == null) ? ("[-] subnodes: " + "\n") : ("+" + location + " subnodes: "));
            for (String s : subnodes.keySet()) {
                temp.append(s).append("\n");
            }
            return temp.toString();
        }

    }

    @Override
    public void addRoute(String path) {
        String[] ds = checkAddFormat(path);
        if (ds == null || ds.length == 0) {
            return;
        }
        int i = 0;
        Node curNode = root;
        while (i < ds.length) {
            if (!curNode.subnodes.containsKey(ds[i])) {
                Node newNode = new Node();
                curNode.subnodes.put(ds[i], newNode); // 新建节点
                curNode = newNode;
            } else {
                // 复用节点
                curNode = curNode.subnodes.get(ds[i]);
            }
            i++;
            if (i == ds.length) {
                curNode.setLocation(path);
            }
        }
    }

    @Override
    public void delRoute(String path) {
        if (StringUtils.isBlank(path) || path.charAt(0) != '/') {
            return;
        }
        int i = 0;
        Node curNode = this.root;
        String[] ds = checkAddFormat(path);
        while (i < ds.length - 1) {
            curNode = curNode.subnodes.get(ds[i]);
            i++;
            if (curNode == null) {
                logger.debug("can not find location: %s", path);
                return;
            }
        }
        Node nextNode = curNode.subnodes.get(ds[ds.length - 1]);
        if (nextNode == null) {
            // throw new Exception("path not found for : " + d);
            logger.debug("can not find location: %s (last index failed)");
            return;
        }
        if (nextNode.subnodes.size() > 0) {
            nextNode.setLocation(null);
        } else {
            curNode.subnodes.remove(ds[ds.length - 1]);
        }
    }

    @Override
    public void updateRoute(String o, String n) {
        this.delRoute(o);
        this.addRoute(n);
    }

    @Override
    public RouteInfo match(String path) {
        String[] ds = path.substring(1).split("/");
        if (ds == null || ds.length == 0) {
            return null;
        }
        Node matchedNode = matchNode(root, ds);
        if (matchedNode == null) {
            return null;
        }
        RouteInfo route = new RouteInfo();
        route.setRoute(matchedNode.location);
        return route;
    }

    private Node matchNode(Node node, String[] ds) {
        int i = 0;
        Node lastSet = null, curNode = node, nextNode;
        do {
            if (curNode.location != null) {
                lastSet = curNode;
            }
            Node nextNode0 = curNode.subnodes.get("?");
            Node nextNode1 = curNode.subnodes.get(ds[i]);

            nextNode1 = lastRemoveSuffixMatches(curNode, nextNode1, i == ds.length-1, ds[i]);

            if (nextNode0 == null && nextNode1 == null) {
                nextNode = null;
            } else if (nextNode0 == null && nextNode1 != null) {
                nextNode = nextNode1;
            } else if (nextNode0 != null && nextNode1 == null) {
                nextNode = nextNode0;
            } else {
                Node c0 = matchNode(nextNode0, Arrays.copyOfRange(ds, i + 1, ds.length));
                if (c0 != null) {
                    return c0;
                } else {
                    Node c1 = matchNode(nextNode1, Arrays.copyOfRange(ds, i + 1, ds.length));
                    if (c1 != null) {
                        return c1;
                    } else {
                        nextNode = null;
                    }
                }
            }
            if (nextNode != null) {
                curNode = nextNode;
            }
            i++;
        } while (i != ds.length && nextNode != null);
        if (curNode.location != null) {
            lastSet = curNode;
        }
        return lastSet;
    }

    private String[] checkAddFormat(String data) {
        if (StringUtils.isBlank(data)) {
            return null;
        } else if ('/' == data.charAt(0)) {
            String[] ds = data.split("/");
            return ds.length > 0 ? Arrays.copyOfRange(ds, 1, ds.length) : ds;
        } else {
            return null;
        }
    }

    /*public void addRoute(String path) {
        String[] ds = checkAddFormat(path);
        if (ds == null) {
            return;
        }
        Node curNode = this.root;
        int i = 0;
        if (ds.length > 0) {
            while (i < ds.length) {
                Node tempNode = curNode.subnodes.get(ds[i]);
                if (tempNode == null) {
                    int k = i;
                    while (k < ds.length) {
                        Node newNode = new Node();
                        curNode.subnodes.put(ds[k], newNode);
                        curNode = newNode;
                        k++;
                    }
                    curNode.setLocation(path);
                    break;
                } else {
                    curNode = tempNode;
                    i++;
                    if (i == ds.length) {
                        curNode.setLocation(path);
                    }
                }
            }
        }

    }*/

    // 对 送检URL最后一段 尝试去除后缀匹配.
    private Node lastRemoveSuffixMatches(Node current, Node next, boolean lastParagraph, String ds) {
        /********************************************************************
         * 匹配规则：
         *    管理后台录入的location    送检的URL        预期结果
         *    含后缀                   含后缀           精确匹配
         *    不含后缀                 含任意后缀        尝试去除后缀精确匹配
         ********************************************************************/
        if (next == null && lastParagraph) {
            int index = ds.indexOf('.');
            if (index > 0) {
                // ds含有后缀, 去除后缀后再次尝试匹配
                next = current.subnodes.get(ds.substring(0, index));
            }
        }
        return next;
    }
}
